import { find, pick, reduce } from 'lodash';

function fixLegendContainer(plotlyElement) {
  const legend = plotlyElement.querySelector('.legend');
  if (legend) {
    let node = legend.parentNode;
    while (node) {
      if (node.tagName.toLowerCase() === 'svg') {
        node.style.overflow = 'visible';
        break;
      }
      node = node.parentNode;
    }
  }
}

export default function applyLayoutFixes(plotlyElement, layout, updatePlot) {
  // update layout size to plot container
  layout.width = Math.floor(plotlyElement.offsetWidth);
  layout.height = Math.floor(plotlyElement.offsetHeight);

  const transformName = find([
    'transform',
    'WebkitTransform',
    'MozTransform',
    'MsTransform',
    'OTransform',
  ], prop => prop in plotlyElement.style);

  if (layout.width <= 600) {
    // change legend orientation to horizontal; plotly has a bug with this
    // legend alignment - it does not preserve enough space under the plot;
    // so we'll hack this: update plot (it will re-render legend), compute
    // legend height, reduce plot size by legend height (but not less than
    // half of plot container's height - legend will have max height equal to
    // plot height), re-render plot again and offset legend to the space under
    // the plot.
    layout.legend = {
      orientation: 'h',
      // locate legend inside of plot area - otherwise plotly will preserve
      // some amount of space under the plot; also this will limit legend height
      // to plot's height
      y: 0,
      x: 0,
      xanchor: 'left',
      yanchor: 'bottom',
    };

    // set `overflow: visible` to svg containing legend because later we will
    // position legend outside of it
    fixLegendContainer(plotlyElement);

    updatePlot(plotlyElement, pick(layout, ['width', 'height', 'legend'])).then(() => {
      const legend = plotlyElement.querySelector('.legend'); // eslint-disable-line no-shadow
      if (legend) {
        // compute real height of legend - items may be split into few columnns,
        // also scrollbar may be shown
        const bounds = reduce(legend.querySelectorAll('.traces'), (result, node) => {
          const b = node.getBoundingClientRect();
          result = result || b;
          return {
            top: Math.min(result.top, b.top),
            bottom: Math.max(result.bottom, b.bottom),
          };
        }, null);
        // here we have two values:
        // 1. height of plot container excluding height of legend items;
        //    it may be any value between 0 and plot container's height;
        // 2. half of plot containers height. Legend cannot be larger than
        //    plot; if legend is too large, plotly will reduce it's height and
        //    show a scrollbar; in this case, height of plot === height of legend,
        //    so we can split container's height half by half between them.
        layout.height = Math.floor(Math.max(
          layout.height / 2,
          layout.height - (bounds.bottom - bounds.top),
        ));
        // offset the legend
        legend.style[transformName] = 'translate(0, ' + layout.height + 'px)';
        updatePlot(plotlyElement, pick(layout, ['height']));
      }
    });
  } else {
    layout.legend = {
      orientation: 'v',
      // vertical legend will be rendered properly, so just place it to the right
      // side of plot
      y: 1,
      x: 1,
      xanchor: 'left',
      yanchor: 'top',
    };

    const legend = plotlyElement.querySelector('.legend');
    if (legend) {
      legend.style[transformName] = null;
    }

    updatePlot(plotlyElement, pick(layout, ['width', 'height', 'legend']));
  }
}
